package be.vanbaelvandaele.tuxremote;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.widget.Toast;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

public class SshService {

	private static SshService instance;

	private String user;
	private String pass;
	private String host;
	private int port;

	private Session session;
	private String dbusid;
	private String dbusProcess;
	
	private Context context;
	
	private Handler mHandler;
	
	private Dialog connectionDialog;
	
	private Runnable onConnectionEstablished = new Runnable() {
		public void run() {
			hideConnectionDialog();
			Toast t = Toast.makeText(context, "Connection established.", Toast.LENGTH_LONG);
			t.show();
		}
	};
	
	private Runnable onConnectionFailed = new Runnable() {
		
		@Override
		public void run() {
				hideConnectionDialog();
				Toast t = Toast.makeText(context, "Connection failed.", Toast.LENGTH_LONG);
				t.show();
		}
	};

	public static SshService get(Context c) {
		if (instance == null) {
			instance = new SshService();
		}
		SharedPreferences prefs = c.getSharedPreferences(
				Constants.SHARED_USER_PREFERENCES, 0);

		instance.user = prefs.getString("username", "");
		instance.pass = prefs.getString("password", "");
		instance.host = prefs.getString("sshhost", "");
		instance.port = prefs.getInt("sshport", Constants.DEFAULT_SSH_PORT);
		instance.dbusProcess = prefs.getString("dbusProcess", Constants.DEFAULT_DBUS_PROCESS);
		instance.setContext(c);
		return instance;
	}

	private SshService() {
		user = "";
		pass = "";
		host = "";
		port = 22;
		
		mHandler = new Handler();
	}

	private Session getSession() throws Exception {
		if (session == null) {
			connect();
		}

		if (!session.isConnected()) {
			connect();
		}

		return session;
	}

	private void connect() throws JSchException, Exception {
		showConnectionDialog();
		
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
				JSch js = new JSch();
				session = js.getSession(user, host, port);
				session.setPassword(pass);
				session.setConfig("StrictHostKeyChecking", "no");
				session.connect(30000);
				mHandler.post(onConnectionEstablished);

				SshService.this.dbusid = getDbusid();
				} catch (Exception e) {
					mHandler.post(onConnectionFailed);
				}
			}
		});
		thread.start();
		
	}
	
	

	private void showConnectionDialog() {
		connectionDialog = ProgressDialog.show(context, "Connecting",
				"Connecting to " + host + "...", true);
	}
	
	private void hideConnectionDialog() {
		if (connectionDialog != null) {
			connectionDialog.dismiss();
		}
	}

	private String getDbusid() throws Exception {
		Channel channel = getSession().openChannel("exec");
		((ChannelExec) channel)
				.setCommand("cat /proc/$(pidof " + dbusProcess + ")/environ | tr '\\0' '\\n' | grep DBUS_SESSION_BUS_ADDRESS | cut -d '=' -f2-");

		BufferedReader in = new BufferedReader(new InputStreamReader(channel
				.getInputStream()));
		channel.connect();

		String result = "";
		String line = in.readLine();
		while (line != null) {
			result += line;
			line = in.readLine();
		}
		return result;
	}

	/**
	 * Main method for executing remote commands Prepends commands with an
	 * export command to attach the remote session to the local dbus session
	 * 
	 * @param command
	 * @return
	 */
	public String executeCommand(String command) {
		String result = "";
		String export = "export DBUS_SESSION_BUS_ADDRESS=" + dbusid + "";
		try {

			Channel channel = getSession().openChannel("exec");
			((ChannelExec) channel).setCommand(export + " ; " + command);

			BufferedReader in = new BufferedReader(new InputStreamReader(
					channel.getInputStream()));
			channel.connect();

			result = "";
			String line = in.readLine();
			while (line != null) {
				if (!result.equals("")) {
					result += "\n";
				}
				result += line;
				line = in.readLine();
			}
		} catch (Exception e) {
			return e.getMessage();
		}
		return result;
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPass() {
		return pass;
	}

	public void setPass(String pass) {
		this.pass = pass;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}


	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

}
